\section{按位运算注意事项}
\subsection{补码(Two's Complement)}
应区分“\textbf{补码运算}”和“\textbf{补码表示}”。

\textbf{补码运算}为反码(One's Complement)运算加1。

正数的\textbf{补码表示}等同于其二进制表示，而正数的\textbf{补码运算}结果为一负数的\textbf{补码表示}。\textbf{补码运算}相当于求该数的相反数的\textbf{补码表示}。
\textbf{补码表示}的范围是$-2^{N-1}$到$2^{N-1}-1$。而反码(One's Complement)的表示范围是$-(2^{N-1}-1)$到$2^{N-1}-1$

\textbf{补码运算}的溢出会被忽略。对零作\textbf{补码运算}，会发生上溢，忽略上溢后仍是零。

一个数同其\textbf{补码运算}结果(等同于其相反数的\textbf{补码表示})相加(作无符号加法)，结果为$2^N$。

一个数同其反码运算结果相加(作无符号加法)，结果为$2^N-1$。


\subsection{负数右移}
\cite{krc}A.7.8。
移位运算符<<和>>遵循从左到右的结合性。每个运算符的各操作数都必须为整型,并且遵循整型提升原则。结果的类型是提升后的左操作数的类型。如果右操作数为负值,或者大于或等于左操作数类型的位数,则结果没有定义。

在对unsigned 类型的无符号值进行右移位时,左边空出的部分将用0填补;当对 signed 类型的带符号值进行右移时,某些机器将对左边空出的部分用符号位填补(即“算术移位”),而另一些机器则对左边空出的部分用0填补(即“逻辑移位”)。 

unsigned char类型的0xA2右移一位得0x51，而char类型的0xA2右移1位可能为0xD1(gcc验证)。

\subsection{二进制反码运算符}
一元运算符~的操作数必须是整型,结果为操作数的二进制反码。在运算过程中需要对操作数进行整型提升。如果操作数为无符号类型,则结果为提升后的类型能够表示的最大值减去操作数的值而得到的结果值。如果操作数为带符号类型,则结果的计算方式为:将提升后的操作数转换为相应的无符号类型,使用运算符~计算反码,再将结果转换为带符号类型。结果的类型为提升后的操作数的类型。

\subsection{台湾某CPU厂商面试题}
\begin{lstlisting}
    #include <iostream>
    using namespace std;
    int main()
    {
	unsigned char a = 0xA5;
	unsigned char b = ~a>>4+1;
	//cout << b << endl;
	printf(``b=%d\n'', b);
	return 0;
    }
\end{lstlisting}

本题有多处陷阱，一是移位运算的优先级很低，低于加法，因此~a实际上是右移5位，而非右移4位再加1；第二，a取反时应作整型提升，变为有符号数int型；第三，有符号数右移运算的结果实际上取决于实现，本题不妥；第四，b作为无符号数，打印前整型提升，却也不会将符号位进行扩展。

a整型提升后为0x000000a5，取反后为0xffffff5a，假设是算数移位，需要符号扩展，则右移5位得0xfffffffa，b的值为0xfa，整型提升后的值为0x000000fa。按照\%d打印为250。


如果将b强制类型转换为char型，再打印(\lstinline!printf(``%d\n'', (char)b);!)，则整型提升结果为0xfffffffa，打印结果为-6(将任何整数转换为带符号类型时, 如果它可以在新类型中表示出来, 则其值保持不变, 否则它的值同具体的实现有关, 参\cite{self}\ref{typeConv}, \cite{krc}A.6.2)。 

对b的打印也这样理解:\%d如实打印参数的符号，0xfa作为char类型为负数-6，作为unsigned char则为无符号数250，故而有上述结果。













